﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Noris.Tools.TraceVisualiser.Components;
using Noris.Tools.TraceVisualiser.Support;
using Noris.Tools.TraceVisualiser.TraceData;
using Noris.Tools.TraceVisualiser.BlockingVisualiser.Forms;

namespace Noris.Tools.TraceVisualiser.Green.SqlServer
{
    /// <summary>
    /// 
    /// </summary>
    public class LockVisualiser : IDisposable
    {
        protected GraphElementEventArgs GraphElementData { get; set; }
        protected EventInfo CurrentEventInfo { get; set; }
        protected GID RowGid;
        protected DateTime CurrentEventTime;
        /// <summary>
        /// Hlavní balík s daty
        /// </summary>
        private MainInfo CurrentTraceData
        {
            get
            {
                TraceDataSource traceDataSource = this.GraphElementData.DataSource as TraceDataSource;
                if (traceDataSource == null) return null;
                return traceDataSource.Main;
            }
        }
        /// <summary>
        /// Aktuálně zobrazený SQL event deadlock graph
        /// </summary>
        private EventItem SqlEventItem
        {
            get
            {
                EventItem eventItem = this.CurrentEventInfo.EventItem as EventItem;
                return eventItem;
            }
        }
        /// <summary>
        /// Reference na LockForm
        /// </summary>
        protected LockForm Form;
        /// <summary>
        /// Zobrazí aktuální graf
        /// </summary>
        public void ShowForm(GraphElementEventArgs args)
        {
            this.GraphElementData = args;
            this.CurrentEventInfo = args.DataElement as EventInfo;
            this.RowGid = args.DataRow.GId;

            try
            {
                using (LockForm lockForm = new LockForm())
                {
                    this.Form = lockForm;
                    lockForm.FindOtherEvent += this._Form_FindOtherEvent;
                    this.ShowCurrentDeadlock();
                    lockForm.ShowDialog(Steward.MainForm);
                }
            }
            finally
            {
                this.Form = null;
            }
        }
        /// <summary>
        /// Najde a do formuláře zobrazí data aktuálního grafu
        /// </summary>
        /// <param name="sForm"></param>
        private void ShowCurrentDeadlock()
        {
            EventItem sqlEventItem = this.SqlEventItem;
            string textData = sqlEventItem.TextData;
            TimeRange time = sqlEventItem.Time;

            this.CurrentEventTime = time.End;
            this.Form.ShowDeadlockLockInfoFromXml(this.CurrentEventInfo, textData, this.CurrentEventTime);
        }
        /// <summary>
        /// Událost, kdy uživatel chce zobrazit sousední událost
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        void _Form_FindOtherEvent(object sender, LockFormEventArgs args)
        {
            // Najdu si node, který nese informaci o deadlocku:
            TraceData.NodeInfo nodeInfo = this.CurrentTraceData.FindNode(this.RowGid);
            if (nodeInfo == null) return;

            // Najdu si eventy, které vyhovují směru a datu a deadlock grafu:
            DateTime time = this.CurrentEventTime;
            List<TraceData.EventInfo> eventList = nodeInfo.Elements.Values
                .OfType<TraceData.EventInfo>()
                .Where(ei => _ContainSqlDeadlock(ei, args.NextEvent, time))
                .ToList();

            if (eventList.Count == 0) return;

            // Setřídím eventy podle času:
            TraceData.EventInfo eventInfo = null;
            eventList.Sort(TraceData.EventInfo.CompareByBegin);
            switch (args.NextEvent)
            {
                case NextEventSide.Previous:
                    eventInfo = eventList[eventList.Count - 1];
                    break;
                case NextEventSide.Next:
                    eventInfo = eventList[0];
                    break;
                default:
                    return;
            }

            // Uložím si nalezenou událost:
            this.CurrentEventInfo = eventInfo;

            // Zobrazím ji:
            this.ShowCurrentDeadlock();
        }
        /// <summary>
        /// Určí, zda daná událost popisuje deadlock
        /// </summary>
        /// <param name="eventInfo"></param>
        /// <param name="nextEventSide"></param>
        /// <param name="time"></param>
        /// <returns></returns>
        private bool _ContainSqlDeadlock(TraceData.EventInfo eventInfo, NextEventSide nextEventSide, DateTime time)
        {
            if (eventInfo == null) return false;

            EventItem sqlEventItem = eventInfo.EventItem as EventItem;
            if (sqlEventItem == null) return false;

            if (sqlEventItem.SqlEventId != EventItem.SQLTRACE_EVENT_DeadlockGraph) return false;
            
            DateTime eventTime = sqlEventItem.Time.End;
            switch (nextEventSide)
            {
                case NextEventSide.Previous:
                    if (eventTime >= time) return false;
                    break;
                case NextEventSide.Next:
                    if (eventTime <= time) return false;
                    break;
                default:
                    return false;
            }

            return true;
        }
        #region IDisposable Members
        void IDisposable.Dispose()
        {
            this.GraphElementData = null;
            this.CurrentEventInfo = null;
        }
        #endregion
    }
}
